package com.tianji.learning.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.cache.CategoryCache;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.client.search.SearchClient;
import com.tianji.api.client.user.UserClient;
import com.tianji.api.dto.course.CataSimpleInfoDTO;
import com.tianji.api.dto.course.CourseSimpleInfoDTO;
import com.tianji.api.dto.user.UserDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.exceptions.DbException;
import com.tianji.common.utils.*;
import com.tianji.learning.domain.dto.QuestionFormDTO;
import com.tianji.learning.domain.po.InteractionQuestion;
import com.tianji.learning.domain.po.InteractionReply;
import com.tianji.learning.domain.query.QuestionAdminPageQuery;
import com.tianji.learning.domain.query.QuestionPageQuery;
import com.tianji.learning.domain.vo.QuestionAdminVO;
import com.tianji.learning.domain.vo.QuestionVO;
import com.tianji.learning.mapper.InteractionQuestionMapper;
import com.tianji.learning.mapper.InteractionReplyMapper;
import com.tianji.learning.service.IInteractionQuestionService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 互动提问的问题表 服务实现类
 * </p>
 *
 * @author huanmo
 * @since 2025-09-18
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class InteractionQuestionServiceImpl extends ServiceImpl<InteractionQuestionMapper, InteractionQuestion> implements IInteractionQuestionService {

    private final InteractionReplyMapper replyMapper;
    private final UserClient userClient;
    private final CourseClient courseClient;
    private final SearchClient searchClient;
    private final CatalogueClient catalogueClient;
    private final CategoryCache categoryCache;
    @Qualifier("stringRedisTemplate")
    private final StringRedisTemplate redisTemplate;
    @Qualifier("redisObjectTemplate")
    private final RedisTemplate<String, Object> redisObjectTemplate;
    private final static String CATEGORY_KEY_TEMPLATE = "categoryNames";
    private static final String USER_CACHE_KEY_PREFIX = "user:info:";
    private static final long REDIS_EXPIRE_MINUTES = 30;
    // 随机范围5分钟
    private static final long REDIS_EXPIRE_RANDOM_RANGE = 5;

    @Autowired
    @Qualifier("queryTaskExecutor")
    private Executor queryExecutor;

    @Override
    @Transactional
    public void saveQuestion(QuestionFormDTO questionDTO) {
        // 1.获取登录用户
        Long userId = UserContext.getUser();
        // 2.数据转换
        InteractionQuestion question = BeanUtils.toBean(questionDTO, InteractionQuestion.class);
        // 3.补充数据
        question.setUserId(userId);
        // 4.保存问题
        save(question);
    }

    /**
     * 性能优化：见 com/tianji/learning/service/impl/LearningLessonServiceImpl.java/createLearningPlan()
     */
    @Override
    @Transactional
    public void updateQuestion(QuestionFormDTO questionDTO, Long id) {
        InteractionQuestion question = new InteractionQuestion();
        question.setId(id);
        question.setTitle(questionDTO.getTitle());
        question.setDescription(questionDTO.getDescription());
        question.setAnonymity(questionDTO.getAnonymity());
        updateById(question);
    }

    @Override
    public PageDTO<QuestionVO> queryQuestionPage(QuestionPageQuery query) {
        // 1.参数校验，课程id和小节id不能都为空
        Long courseId = query.getCourseId();
        Long sectionId = query.getSectionId();
        if (courseId == null && sectionId == null) {
            throw new BadRequestException("课程id和小节id不能都为空");
        }

        // 2.分页查询
        Page<InteractionQuestion> page = lambdaQuery()
                // 只读取所需字段
                // 排除 description 字段：查询结果中不包含 description 列的数据
                // 减少数据传输：避免返回大文本字段，提高查询性能
                // 保护敏感数据：防止意外返回不需要的大字段数据
                .select(InteractionQuestion.class, info -> !"description".equals(info.getProperty()))
                .eq(BooleanUtils.isTrue(query.getOnlyMine()), InteractionQuestion::getUserId, UserContext.getUser())
                .eq(courseId != null, InteractionQuestion::getCourseId, courseId)
                .eq(sectionId != null, InteractionQuestion::getSectionId, sectionId)
                .eq(InteractionQuestion::getHidden, false)
                .page(query.toMpPageDefaultSortByCreateTimeDesc());
        List<InteractionQuestion> records = page.getRecords();
        if (CollUtils.isEmpty(records)) {
            return PageDTO.empty(page);
        }

        // 3.根据id查询提问者和最近一次回答的信息
        Set<Long> userIds = new HashSet<>();
        Set<Long> answerIds = new HashSet<>();
        // 先收集所有的id至set集合，然后分别访问一次数据库获取全部的user和answer
        // 3.1.得到问题当中的提问者id和最近一次回答的id
        for (InteractionQuestion q : records) {
            // 只查询非匿名的问题
            if (!q.getAnonymity()) {
                userIds.add(q.getUserId());
            }
            answerIds.add(q.getLatestAnswerId());
        }
        // 3.2.根据id查询最近一次回答
        answerIds.remove(null);
        Map<Long, InteractionReply> replyMap = new HashMap<>(answerIds.size());
        if (CollUtils.isNotEmpty(answerIds)) {
            List<InteractionReply> replies = replyMapper.selectBatchIds(answerIds);
            for (InteractionReply reply : replies) {
                replyMap.put(reply.getId(), reply);
                // 匿名用户不做查询
                if (!reply.getAnonymity()) {
                    userIds.add(reply.getUserId());
                }
            }
        }

        // 3.3.根据id查询用户信息（提问者）
        userIds.remove(null);
        Map<Long, UserDTO> userMap = new HashMap<>(userIds.size());
        if (CollUtils.isNotEmpty(userIds)) {
            List<UserDTO> users = userClient.queryUserByIds(userIds);
            userMap = users.stream()
                    .collect(Collectors.toMap(UserDTO::getId, u -> u));
        }

        // 4.封装VO
        List<QuestionVO> voList = new ArrayList<>(records.size());
        for (InteractionQuestion r : records) {
            // 4.1.将PO转为VO
            QuestionVO vo = BeanUtils.copyBean(r, QuestionVO.class);
            vo.setUserId(null);
            voList.add(vo);
            // 4.2.封装提问者信息
            if (!r.getAnonymity()) {
                UserDTO userDTO = userMap.get(r.getUserId());
                if (userDTO != null) {
                    vo.setUserId(userDTO.getId());
                    vo.setUserName(userDTO.getName());
                    vo.setUserIcon(userDTO.getIcon());
                }
            }
            // 4.3.封装最近一次回答的信息
            InteractionReply reply = replyMap.get(r.getLatestAnswerId());
            if (reply != null) {
                vo.setLatestReplyContent(reply.getContent());
                // 匿名用户直接忽略
                if (!reply.getAnonymity()) {
                    UserDTO user = userMap.get(reply.getUserId());
                    vo.setLatestReplyUser(user.getName());
                }
            }
        }

        return PageDTO.of(page, voList);
    }

    @Override
    public QuestionVO queryQuestionById(Long id) {
        // 1.根据id查询数据
        InteractionQuestion question = getById(id);
        // 2.数据校验
        if (question == null || question.getHidden()) {
            // 没有数据或者是被隐藏了
            return null;
        }
        // 3.查询提问者信息
        UserDTO user = null;
        if (!question.getAnonymity()) {
            user = userClient.queryUserById(question.getUserId());
        }
        // 4.封装VO
        QuestionVO vo = BeanUtils.copyBean(question, QuestionVO.class);
        if (user != null) {
            vo.setUserName(user.getName());
            vo.setUserIcon(user.getIcon());
        }
        return vo;
    }

    @Override
    @Transactional
    public void deleteQuestion(Long id) {
        // 1.根据id查询问题
        InteractionQuestion question = getById(id);
        if (question == null) {
            log.error("问题不存在，id: {}", id);
            throw new BadRequestException("该问题不存在");
        }
        // 2.判断是否是当前用户提问的
        Long currentUserId = UserContext.getUser();
        if (!question.getUserId().equals(currentUserId)) {
            log.error("用户{}无权限删除问题{}，问题属于用户{}", currentUserId, id, question.getUserId());
            throw new BadRequestException("无法删除别人的问题");
        }
        // 3.删除该问题下的所有回答（级联删除）
        // 使用 replyMapper 实例调用 lambdaQuery()
        replyMapper.delete(
                new LambdaQueryWrapper<InteractionReply>()
                        .eq(InteractionReply::getQuestionId, question.getId()));
        log.debug("已删除问题{}的回答", id);
        // 4.再删除问题本身
        boolean deleted = removeById(id);
        if (!deleted) {
            log.error("删除问题失败，id: {}", id);
            throw new DbException("删除问题失败");
        }
        log.debug("用户{}成功删除问题{}", currentUserId, id);
    }

    @Override
    @Transactional
    public void isHidden(long id, boolean hidden) {
        // 1.根据id查询问题
        InteractionQuestion question = getById(id);
        if (question == null) {
            throw new BadRequestException("该问题不存在");
        }
        // 2.如果状态已经相同，直接返回
        if (question.getHidden().equals(hidden)) {
            log.debug("问题{}已经是{}状态，无需操作", id, hidden ? "隐藏" : "显示");
            return;
        }
        // 3.先隐藏问题下的回答及评论（如果有的话）
        LambdaUpdateWrapper<InteractionReply> replyWrapper = new LambdaUpdateWrapper<>();
        replyWrapper.eq(InteractionReply::getQuestionId, id)
                .set(InteractionReply::getHidden, hidden);
        replyMapper.update(null, replyWrapper);
        // 4.更新问题本身的隐藏状态
        boolean updateQuestion = lambdaUpdate()
                .eq(InteractionQuestion::getId, id)
                .set(InteractionQuestion::getHidden, hidden)
                .update();
        if (!updateQuestion) {
            log.error("更新问题[{}]隐藏状态失败", id);
            throw new DbException("更新问题状态失败");
        }
    }

    /**
     * 多线程并行 + 多级缓存查询
     */
    @Override
    public QuestionAdminVO queryQuestionByIdAdmin(long id) {
        // 1.查询问题详情
        InteractionQuestion question = getById(id);
        if (question == null) {
            return null;
        }
        // 2.并行查询相关数据
        CompletableFuture<UserDTO> userFuture = CompletableFuture.supplyAsync(() -> {
            try {
                return !question.getAnonymity() ? queryUserByIdWithCache(question.getUserId()) : null;
            } catch (Exception e) {
                log.warn("查询用户信息失败，userId: {}", question.getUserId(), e);
                return null;
            }
        }, queryExecutor);

        CompletableFuture<CourseSimpleInfoDTO> courseFuture = CompletableFuture.supplyAsync(() -> {
            try {
                List<CourseSimpleInfoDTO> courseList = courseClient.getSimpleInfoList(Collections.singletonList(question.getCourseId()));
                return CollUtils.isNotEmpty(courseList) ? courseList.get(0) : null;
            } catch (Exception e) {
                log.warn("查询课程信息失败，courseId: {}", question.getCourseId(), e);
                return null;
            }
        }, queryExecutor);

        CompletableFuture<Map<Long, String>> cataFuture = CompletableFuture.supplyAsync(() -> {
            try {
                Set<Long> cataIds = new HashSet<>();
                cataIds.add(question.getChapterId());
                cataIds.add(question.getSectionId());

                List<CataSimpleInfoDTO> catas = catalogueClient.batchQueryCatalogue(cataIds);
                return CollUtils.isNotEmpty(catas) ?
                        catas.stream().collect(Collectors.toMap(CataSimpleInfoDTO::getId, CataSimpleInfoDTO::getName)) :
                        Collections.emptyMap();
            } catch (Exception e) {
                log.warn("查询章节信息失败，cataIds: {}", Set.of(question.getChapterId(), question.getSectionId()), e);
                return Collections.emptyMap();
            }
        }, queryExecutor);

        // 3.等待所有查询完成
        try {
            UserDTO user = userFuture.get(3, TimeUnit.SECONDS);
            CourseSimpleInfoDTO courseInfo = courseFuture.get(3, TimeUnit.SECONDS);
            Map<Long, String> cataMap = cataFuture.get(3, TimeUnit.SECONDS);
            // 4.封装VO
            QuestionAdminVO vo = BeanUtils.copyBean(question, QuestionAdminVO.class);
            if (user != null) {
                vo.setUserName(user.getName());
            }
            vo.setStatus(1);
            // 获取分类名称
            List<Long> categoryIds = courseInfo != null ? courseInfo.getCategoryIds() : Collections.emptyList();
            vo.setCategoryName(getCategoryNamesWithMultiLevelCache(categoryIds));
            return vo;
        } catch (Exception e) {
            log.error("查询问题详情失败，id: {}", id, e);
            throw new BizIllegalException("查询问题详情失败");
        }
    }

    /**
     * 多线程并行 + 多级缓存查询
     */
    @Override
    public PageDTO<QuestionAdminVO> queryQuestionPageAdmin(QuestionAdminPageQuery query) {
        // 1.处理课程名称，得到课程id
        List<Long> courseIds = null;
        if (StringUtils.isAllNotBlank(query.getCourseName())) {
            courseIds = searchClient.queryCoursesIdByName(query.getCourseName());
            if (CollUtils.isEmpty(courseIds)) {
                return PageDTO.empty(0L, 0L);
            }
        }
        // 2.分页查询
        Integer status = query.getStatus();
        LocalDateTime begin = query.getBeginTime();
        LocalDateTime end = query.getEndTime();
        Page<InteractionQuestion> page = lambdaQuery()
                .in(courseIds != null, InteractionQuestion::getCourseId, courseIds)
                .eq(status != null, InteractionQuestion::getStatus, status)
                .gt(begin != null, InteractionQuestion::getCreateTime, begin)
                .lt(end != null, InteractionQuestion::getCreateTime, end)
                .page(query.toMpPageDefaultSortByCreateTimeDesc());
        List<InteractionQuestion> records = page.getRecords();
        if (CollUtils.isEmpty(records)) {
            return PageDTO.empty(page);
        }
        // 3.准备VO需要的数据：用户、课程、章节、分类
        Set<Long> userIds = new HashSet<>();
        Set<Long> cIds = new HashSet<>();
        Set<Long> cataIds = new HashSet<>();
        // 3.1.获取各种数据的id集合
        for (InteractionQuestion r : records) {
            userIds.add(r.getUserId());
            cIds.add(r.getCourseId());
            cataIds.add(r.getChapterId());
            cataIds.add(r.getSectionId());
        }

        // 3.2.并行查询相关数据
        CompletableFuture<Map<Long, UserDTO>> userFuture = CompletableFuture.supplyAsync(() -> {
            try {
                List<UserDTO> users = queryUserByIdsWithCache(userIds);
                return CollUtils.isNotEmpty(users) ?
                        users.stream().collect(Collectors.toMap(UserDTO::getId, u -> u)) :
                        Collections.emptyMap();
            } catch (Exception e) {
                log.warn("查询用户信息失败，userIds: {}", userIds, e);
                return Collections.emptyMap();
            }
        }, queryExecutor);
        CompletableFuture<Map<Long, CourseSimpleInfoDTO>> courseFuture = CompletableFuture.supplyAsync(() -> {
            try {
                List<CourseSimpleInfoDTO> cInfos = courseClient.getSimpleInfoList(cIds);
                return CollUtils.isNotEmpty(cInfos) ?
                        cInfos.stream().collect(Collectors.toMap(CourseSimpleInfoDTO::getId, c -> c)) :
                        Collections.emptyMap();
            } catch (Exception e) {
                log.warn("查询课程信息失败，courseIds: {}", cIds, e);
                return Collections.emptyMap();
            }
        }, queryExecutor);
        CompletableFuture<Map<Long, String>> cataFuture = CompletableFuture.supplyAsync(() -> {
            try {
                List<CataSimpleInfoDTO> catas = catalogueClient.batchQueryCatalogue(cataIds);
                return CollUtils.isNotEmpty(catas) ?
                        catas.stream().collect(Collectors.toMap(CataSimpleInfoDTO::getId, CataSimpleInfoDTO::getName)) :
                        Collections.emptyMap();
            } catch (Exception e) {
                log.warn("查询章节信息失败，cataIds: {}", cataIds, e);
                return Collections.emptyMap();
            }
        }, queryExecutor);

        // 4.等待所有查询完成
        try {
            Map<Long, UserDTO> userMap = userFuture.get(5, TimeUnit.SECONDS);
            Map<Long, CourseSimpleInfoDTO> cInfoMap = courseFuture.get(5, TimeUnit.SECONDS);
            Map<Long, String> catasMap = cataFuture.get(5, TimeUnit.SECONDS);
            // 5. 使用查询结果继续处理...
            // 封装VO
            List<QuestionAdminVO> voList = new ArrayList<>(records.size());
            for (InteractionQuestion r : records) {
                // 5.1.将PO转VO，属性拷贝
                QuestionAdminVO vo = BeanUtils.copyBean(r, QuestionAdminVO.class);
                // Java对象是引用传递
                // 后续的属性设置会反映到已添加到列表的对象中
                voList.add(vo);
                // 5.2.用户信息
                UserDTO user = userMap.get(r.getUserId());
                if (user != null) {
                    vo.setUserName(user.getName());
                }
                // 5.3.课程信息以及分类信息
                CourseSimpleInfoDTO cInfo = cInfoMap.get(r.getCourseId());
                if (cInfo != null) {
                    vo.setCourseName(cInfo.getName());
                    // 处理分类名称的多级缓存
                    String categoryNames = getCategoryNamesWithMultiLevelCache(cInfo.getCategoryIds());
                    vo.setCategoryName(categoryNames);
                }
                // 5.4.章节信息
                vo.setChapterName(catasMap.getOrDefault(r.getChapterId(), ""));
                vo.setSectionName(catasMap.getOrDefault(r.getSectionId(), ""));
            }
            return PageDTO.of(page, voList);
        } catch (TimeoutException e) {
            log.error("查询超时，userIds: {}, courseIds: {}, cataIds: {}", userIds, cIds, cataIds, e);
            throw new BizIllegalException("查询超时，请重试");
        } catch (ExecutionException | InterruptedException e) {
            log.error("并行查询失败", e);
            throw new BizIllegalException("查询失败，请重试");
        }
    }

    /**
     * 批量查询用户信息（带缓存）
     */
    public List<UserDTO> queryUserByIdsWithCache(Set<Long> userIds) {
        if (CollUtils.isEmpty(userIds)) {
            return Collections.emptyList();
        }
        List<UserDTO> result = new ArrayList<>();
        List<Long> missingUserIds = new ArrayList<>();
        // 1. 先从缓存中获取
        for (Long userId : userIds) {
            String cacheKey = getUserCacheKey(userId);
            UserDTO cachedUser = (UserDTO) redisObjectTemplate.opsForValue().get(cacheKey);
            if (cachedUser != null) {
                result.add(cachedUser);
            } else {
                missingUserIds.add(userId);
            }
        }
        // 2. 如果缓存中都有，直接返回
        if (missingUserIds.isEmpty()) {
            log.debug("用户信息全部从缓存中获取，userIds: {}", userIds);
            return result;
        }
        // 3. 缓存中没有的，从服务查询
        log.debug("部分用户信息未命中缓存，从服务查询: {}", missingUserIds);
        List<UserDTO> dbUsers = userClient.queryUserByIds(missingUserIds);
        // 4. 将查询结果放入缓存
        cacheUsers(dbUsers);
        // 5. 合并结果
        result.addAll(dbUsers);
        return result;
    }

    /**
     * 查询单个用户信息（带缓存）
     */
    public UserDTO queryUserByIdWithCache(Long userId) {
        if (userId == null) {
            return null;
        }
        Set<Long> userIds = Collections.singleton(userId);
        List<UserDTO> users = queryUserByIdsWithCache(userIds);
        return CollUtils.isNotEmpty(users) ? users.get(0) : null;
    }

    /**
     * 批量缓存用户信息
     */
    private void cacheUsers(List<UserDTO> users) {
        if (CollUtils.isEmpty(users)) {
            return;
        }
        try {
            for (UserDTO user : users) {
                if (user != null && user.getId() != null) {
                    String cacheKey = getUserCacheKey(user.getId());
                    redisObjectTemplate.opsForValue().set(
                            cacheKey,
                            user,
                            REDIS_EXPIRE_MINUTES + new Random().nextInt(5),
                            TimeUnit.MINUTES
                    );
                }
            }
            log.debug("成功缓存{}个用户信息", users.size());
        } catch (Exception e) {
            log.warn("缓存用户信息失败", e);
        }
    }

    private String getUserCacheKey(Long userId) {
        return USER_CACHE_KEY_PREFIX + userId;
    }

    /**
     * 多级缓存获取分类名称
     */
    private String getCategoryNamesWithMultiLevelCache(List<Long> categoryIds) {
        if (CollUtils.isEmpty(categoryIds)) {
            return "";
        }
        // 生成缓存键
        String cacheKey = generateCategoryCacheKey(categoryIds);
        try {
            // 1. 先查一级缓存（内存）
            String categoryNames = categoryCache.getCategoryNames(categoryIds);
            if (StringUtils.isNotBlank(categoryNames)) {
                log.debug("一级缓存命中: {}", cacheKey);
                return categoryNames;
            }
            // 2. 一级缓存为空，查二级缓存（Redis）
            categoryNames = redisTemplate.opsForValue().get(cacheKey);
            // Redis可能返回null
            if (categoryNames != null) {
                if (StringUtils.isNotBlank(categoryNames)) {
                    log.debug("二级缓存命中: {}", cacheKey);
                    // 回填一级缓存
                    categoryCache.putCategoryNames(categoryIds, categoryNames);
                    log.debug("已从二级缓存获取数据，并更新一级缓存");
                    return categoryNames;
                } else {
                    // 缓存了空值，直接返回
                    log.debug("二级缓存命中空值: {}", cacheKey);
                    return "";
                }
            }
            // 3. 两级缓存都未命中，从数据库加载
            log.debug("缓存未命中，从数据库加载: {}", cacheKey);
            categoryNames = loadCategoryNamesFromDb(categoryIds);
            // 4. 更新缓存（异步操作，避免阻塞主流程）
            updateCacheAsync(cacheKey, categoryIds, categoryNames);
            return categoryNames;
        } catch (Exception e) {
            log.warn("多级缓存获取分类名称失败，直接查询数据库: {}", categoryIds, e);
            // 降级方案：直接查询数据库
            return loadCategoryNamesFromDb(categoryIds);
        }
    }

    /**
     * 生成缓存键（确保相同分类ID集合生成相同的键）
     */
    private String generateCategoryCacheKey(List<Long> categoryIds) {
        if (CollUtils.isEmpty(categoryIds)) {
            return CATEGORY_KEY_TEMPLATE + "empty";
        }
        // 对分类ID排序以确保相同集合生成相同键
        List<Long> sortedIds = new ArrayList<>(categoryIds);
        Collections.sort(sortedIds);
        // 正确的方式拼接字符串
        return CATEGORY_KEY_TEMPLATE + sortedIds.stream()
                .map(String::valueOf)
                .collect(Collectors.joining("-"));
    }

    /**
     * 异步更新缓存
     */
    @Async
    public void updateCacheAsync(String cacheKey, List<Long> categoryIds, String categoryNames) {
        try {
            if (StringUtils.isBlank(categoryNames)) {
                // 空值处理，防止缓存穿透
                categoryNames = "";
            }
            // 更新一级缓存
            categoryCache.putCategoryNames(categoryIds, categoryNames);
            log.debug("已从数据库获取数据，并更新一级缓存");
            // 更新二级缓存（Redis），添加随机过期时间避免雪崩
            long expireTime = REDIS_EXPIRE_MINUTES + new Random().nextInt((int) REDIS_EXPIRE_RANDOM_RANGE);
            redisTemplate.opsForValue().set(
                    cacheKey,
                    categoryNames,
                    expireTime,
                    TimeUnit.MINUTES
            );
            log.debug("缓存更新成功，key: {}, expire: {}分钟", cacheKey, expireTime);
        } catch (Exception e) {
            log.warn("异步更新缓存失败: {}", cacheKey, e);
            // 异步操作失败不影响主流程
        }
    }

    /**
     * 添加缓存清除方法（用于数据变更时）
     */
    public void evictCategoryCache(List<Long> categoryIds) {
        if (CollUtils.isEmpty(categoryIds)) {
            return;
        }
        String cacheKey = generateCategoryCacheKey(categoryIds);
        try {
            // 清除一级缓存
            categoryCache.evictCategoryNames(categoryIds);
            // 清除二级缓存
            redisTemplate.delete(cacheKey);
            log.debug("分类缓存清除成功: {}", cacheKey);
        } catch (Exception e) {
            log.warn("清除分类缓存失败: {}", cacheKey, e);
        }
    }

    private String loadCategoryNamesFromDb(List<Long> categoryIds) {
//        return categoryCache.getCategoryNames(categoryIds);
        return "默认CategoryNames，用于测试多级缓存";
    }
}
